import moment from 'moment'
import { pinyin } from 'pinyin-pro'
import hash from 'hash.js'
import seedrandom from 'seedrandom'
import Color from 'color'
import lodash from 'lodash'
import * as XLSX from 'xlsx'
import Decimal from 'decimal.js'
import { ContactModel, MaterialModel, OrderItemModel, OrderModel } from '~/model'

/**
 * 订单汇总工具类
 */
export class OrderSummary {
  orders: OrderModel[]
  consumer?: string

  items: string[]

  dates: string[]
  consumers: string[]
  batches: number[]

  unitPrices: number[]
  quantityAoa: number[][]
  quantityOfItems: number[]
  amountsOfItem: number[]
  amountsOfBatch: number[]

  totalAmount: number
  totalQuantity: number

  constructor(_orders: OrderModel[], public useFair = true) {
    // 深拷贝数据
    this.orders = lodash.cloneDeep(_orders)

    // 用户数量校验
    const consumers = new Set(this.orders.map(x => x.consumer?.name))
    if (consumers.size !== 1) {
      this.consumer = undefined
    }
    else {
      this.consumer = this.orders[0].consumer!.name
    }

    // 将运费转化成商品
    if (useFair) {
      this.orders.forEach((order) => {
        if (order.needDelivery && order.fare) {
          const _material = new MaterialModel()
          _material.name = '运费'
          const _oim = new OrderItemModel()
          _oim.material = _material
          _oim.unitPrice = 1
          _oim.amount = order.fare
          _oim.quantity = order.fare
          order.items.push(_oim)
        }
      })
    }
    

    // 将商品按照购买次数排序，次数多的在前面
    this.orders.sort((a, b) => a.batch - b.batch)
    this.dates = this.orders.map(x => moment(x.date).format('YYYY-MM-DD HH:mm:ss'))
    this.consumers = this.orders.map(x => x.consumer?.name || '')
    this.batches = this.orders.map(x => x.batch)
    // (订单batch, 商品行) => 商品
    const aoaOrdersItems = this.orders.map(x => x.items)
    // arr: 商品
    const orderItems = lodash.flatten(aoaOrdersItems)
    const DIVIDER = '^^^^^^^^^^^^'
    // 计数：(name, specification, unitPrice)
    const objMapItemCount = lodash.countBy(orderItems, x => `${x.material?.name}${DIVIDER}${x.material?.specification}${DIVIDER}${x.unitPrice}`)

    console.log('objMapItemCount')
    console.log(objMapItemCount)

    // arr: (name, specification, unitPrice, count)
    const nameUnitPriceCountArr = Object.keys(objMapItemCount).map((k) => {
      const arr = k.split(DIVIDER)
      console.log(arr)

      return {
        name: arr[0],
        specification: arr[1],
        unitPrice: Number.parseFloat(arr[2]),
        count: objMapItemCount[k],
      }
    })
    nameUnitPriceCountArr.sort((a, b) => b.count - a.count)

    // console.log('nameUnitPriceCountArr');
    // console.log(nameUnitPriceCountArr);

    // 商品列名
    this.items = nameUnitPriceCountArr.map(x => `${x.name}(${x.specification})`)
    // 商品单价
    this.unitPrices = nameUnitPriceCountArr.map(x => x.unitPrice)

    // 数量矩阵 (batch, line) => quantity
    this.quantityAoa = []
    for (const order of this.orders) {
      // 每个订单的数量向量
      const quantities = [] as number[]
      for (let i = 0; i < this.items.length; i++) {
        const orderLine = order.items.find((x) => {
          return `${x.material?.name}(${x.material?.specification})` === this.items[i]
            && x.unitPrice === this.unitPrices[i]
        })
        if (orderLine) {
          quantities[i] = orderLine.quantity
        }
        else {
          quantities[i] = 0
        }
      }
      this.quantityAoa.push(quantities)
    }

    // 计算每种商品总数量
    this.quantityOfItems = []
    for (let i = 0; i < this.items.length; i++) {
      let sum = 0
      for (let j = 0; j < this.orders.length; j++) {
        sum += this.quantityAoa[j][i] as number
      }
      this.quantityOfItems.push(sum)
    }

    // 计算每种商品总金额
    this.amountsOfItem = this.quantityOfItems.map((item, i) => {
      const a = new Decimal(item)
      const b = new Decimal(this.unitPrices[i])
      return a.mul(b).toNumber()
    })

    // 计算每个批次的总金额
    console.log('this.amountsOfBatch')
    console.log(this.quantityAoa)
    console.log(this.unitPrices)

    this.amountsOfBatch = this.quantityAoa.map((quantityArr) => {
      return quantityArr.map((n, i) => n * this.unitPrices[i]).reduce((acc, cur) => acc + cur, 0)
    })

    this.totalAmount = this.amountsOfItem.reduce((acc, cur) => acc + cur, 0)
    this.totalQuantity = lodash.flatten(this.quantityAoa).reduce((acc, cur) => acc + cur, 0)

    console.log(this)
  }

  public async exportExcelDetail(): Promise<void> {
    type TheCell = string | number

    // 打印表头
    const data = [] as TheCell[][]
    data.push(['时间', '用户', '批次', ...this.items, '金额'])

    // 打印每一行
    for (let i = 0; i < this.orders.length; i++) {
      data.push([this.dates[i], this.consumers[i], this.batches[i], ...this.quantityAoa[i], this.amountsOfBatch[i]])
    }

    // 打印每种（商品，单价）的数量，单价，金额
    data.push(['数量', '', '', ...this.quantityOfItems])
    data.push(['单价', '', '', ...this.unitPrices])
    data.push(['金额', '', '', ...this.amountsOfItem])

    // 计算总计
    const rowTotalAmount: TheCell[] = Array.from({ length: data[0].length })
    rowTotalAmount[0] = '总计'
    rowTotalAmount[rowTotalAmount.length - 1] = this.totalAmount
    data.push(rowTotalAmount)

    const worksheet = XLSX.utils.aoa_to_sheet(data)
    const rOfTotalAmount = data.indexOf(rowTotalAmount)

    // 合并单元格
    const excelMerges = [
      // 数量
      {
        s: { r: rOfTotalAmount - 3, c: 0 },
        e: { r: rOfTotalAmount - 3, c: 2 },
      },
      // 单价
      {
        s: { r: rOfTotalAmount - 2, c: 0 },
        e: { r: rOfTotalAmount - 2, c: 2 },
      },
      // 金额
      {
        s: { r: rOfTotalAmount - 1, c: 0 },
        e: { r: rOfTotalAmount - 1, c: 2 },
      },
      // 总计
      {
        s: { r: rOfTotalAmount, c: 0 },
        e: { r: rOfTotalAmount, c: rowTotalAmount.length - 2 },
      },
    ]
    worksheet['!merges'] = excelMerges

    // 添加颜色样式
    const workbook = XLSX.utils.book_new()
    XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet 1')
    const styles = workbook.Custprops

    // 保存工作簿
    XLSX.writeFileXLSX(workbook, `${this.consumer ?? '多用户'}账单.xlsx`)
  }
}

export abstract class Common {
  public static timeFormat(inp?: moment.MomentInput) {
    return moment(inp).format('YYYY-MM-DD HH:mm:ss')
  }

  // public static async calcBatchOfTheDay(order: OrderModel, newOrder = false) {
  //   const allOrders = await OrderApi.getAll()
  //   // console.log(allOrders)

  //   // 计算顾客当日订单
  //   const orderOfCustomerAndTheDay = allOrders.filter((x) => {
  //     const isTheSamePerson = x.consumer?.id === order.consumer?.id
  //     const _day = (d: number) => Number.parseInt((d / 86400000).toString())
  //     const isTheSameDay = _day(x.date.valueOf()) === _day(order.date.valueOf())
  //     return isTheSamePerson && isTheSameDay
  //   })

  //   // console.warn(orderOfCustomerAndTheDay)

  //   orderOfCustomerAndTheDay.sort((a, b) => a.id - b.id)

  //   if (newOrder) {
  //     return orderOfCustomerAndTheDay.length + 1
  //   }
  //   else {
  //     const index = orderOfCustomerAndTheDay.findIndex(x => x.id === order.id)
  //     return index + 1
  //   }
  // }

  public static getColor(i: number) {
    const colors = [
      'lightpink',
      'lightsalmon',
      'lightgoldenrodyellow',
      'aquamarine',
      'powderblue',
      'lightskyblue',
      'plum',
      'aliceblue',
      'lightsteelblue',
    ]
    return i % colors.length
  }

  static _lastColor = '#000000'
  public static randomColor(seed?: number | string): string {
    let _seed = ''
    if (typeof (seed) === 'undefined') {
      _seed = hash.sha256().update(Common._lastColor + Date.now().toString()).digest('hex')
    }
    else {
      if (typeof (seed) === 'string') {
        _seed = hash.sha256().update(seed).digest('hex')
      }
      else if (typeof (seed) === 'number') {
        _seed = seed.toString()
      }
    }

    const rng = seedrandom(_seed)
    const rn = rng()

    const color = Color(`hsl(${360 * rn}deg, 75%, 50%)`)

    // 随机生成则加大对比度
    if (typeof (seed) === 'undefined') {
      const h1 = color.hue()
      const h0 = Color(Common._lastColor).hue()
      Common._lastColor = color.hex()
      if (Math.abs(h0 - h1) < 30) {
        return Common.randomColor()
      }
    }
    return color.hex()
  }

  public static genAlias(contact: ContactModel) {
    const name = contact.name
    const arr = pinyin(name, { toneType: 'none', type: 'array' })
    const shoupin = arr.map(x => x[0]).join('')
    const alias = `${shoupin}${name}${contact.phone}`

    return alias
  }

  public static genAliasNameOnly(name: string) {
    const arr = pinyin(name, { toneType: 'none', type: 'array' })
    const shoupin = arr.map(x => x[0]).join('')
    const alias = `${shoupin}${name}`

    return alias
  }

  public static totalAmount(order: OrderModel) {
    let sum = order.items.filter(x => x.material!.id > 0).map(x => x.amount).reduce((acc, cur) => acc + cur, 0)
    if (order.needDelivery) {
      sum += order.fare
    }
    return sum
  }

  // public static totalQuantity(order: OrderModel) {
  //   let sum = order.items.filter(x => x.material!.id > 0).map(x => x.quantity).reduce((acc, cur) => acc + cur, 0)
  //   if (order.needDelivery) {
  //     sum += order.fare
  //   }
  //   return sum
  // }

  // static getOrderSummary(_order: Order[]) {

  // }

  // public static async exportSumaryFileForConsumer(consumer: Contact, _order: Order[]) {

  // }
}
